//
//  BNPageView.swift
//  BNUQuoteTrade
//
//  Created by bebull on 2021/12/22.
//

import UIKit
import sqlcipher

protocol BNPageViewDelegate: NSObjectProtocol {
    /// 通知代理，contentView已经发生偏移
    func contentView(_ contentView: UIScrollView, didChanged offset: CGPoint)
}

class BNPageView: UIView {
    
    // MARK: - Properties
    // MARK: Public
    /// 对子控制器的`willMove(toParent:)`、`addChild()`、`didMove(toParent:)`要用
    weak var parentViewController: UIViewController? {
        didSet {
            /// 解决侧滑返回失效的问题
            if let parent = parentViewController, let controller = parent.navigationController, let recognizer = controller.interactivePopGestureRecognizer {
                self.contentView.panGestureRecognizer.require(toFail: recognizer)
            }
        }
    }
    
    weak var delegate: BNPageViewDelegate?

    var viewControllers: [UIViewController] = [] {
        willSet {
            let newCount: Int = newValue.count
            if newCount > 0 {
                for ctrl in self.viewControllers {
                    ctrl.removeFromParent()
                }
                for subviews in self.contentView.subviews {
                    subviews.removeFromSuperview()
                }
            }
            
            if currentIndex >= newCount {
                currentIndex = newCount - 1
            }
            if targetIndex >= newCount {
                targetIndex = newCount - 1
            }
            self.viewControllers = newValue
            
            contentView.contentSize = CGSize(width: CGFloat(newCount) * bounds.size.width, height: contentView.contentSize.height)
        }
    }

    lazy var contentView: UIScrollView = {
        let view = UIScrollView.init(frame: self.bounds)
        view.isPagingEnabled = true
        view.delegate = self
        view.bounces = false
        view.showsHorizontalScrollIndicator = false
        view.showsVerticalScrollIndicator = false
        view.addObserver(self, forKeyPath: "contentOffset", options: .new, context: nil)
        if #available(iOS 11.0, *) {
            view.contentInsetAdjustmentBehavior = .never
        } else {
            view.contentInset = .zero
        }
        return view
    }()
    
    /// tabPabeView使用
    var needLayout: Bool = true

    // MARK: Private
    
    private var currentIndex: Int = 0
    private var targetIndex: Int = 0
    
    
    
    // MARK: - Init
    override init(frame: CGRect) {
        super.init(frame: frame)

        addSubview(contentView)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    deinit {
        contentView.removeObserver(self, forKeyPath: "contentOffset")
    }
    
    // MARK: - Override
    
    override func layoutSubviews() {
        super.layoutSubviews()
        if needLayout {
            needLayout = false
            reloadData()
        }
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "contentOffset", (contentView.isTracking || contentView.isDecelerating) {
            if let change = change, let contentOffset = change[.newKey] as? CGPoint {
                self.delegate?.contentView(contentView, didChanged: contentOffset)
                //contentScrollViewDidChanged(contentOffset)
            }
            
        }
    }
    
    // MARK: - Publics
    
    open func reloadData() {
        contentView.frame = self.bounds
        contentView.contentSize = CGSize(width: CGFloat(self.viewControllers.count) * bounds.size.width, height: contentView.contentSize.height)

        if currentIndex < self.viewControllers.count {
            let currentController = self.viewControllers[self.currentIndex]
            if currentController.parent == nil {
                currentController.willMove(toParent: self.parentViewController)
                parentViewController?.addChild(currentController)
                currentController.didMove(toParent: parentViewController)
                contentView.addSubview(currentController.view)
            }
            currentController.view.frame = CGRect(x: CGFloat(currentIndex) * contentView.bounds.size.width, y: 0, width: contentView.bounds.size.width, height: contentView.bounds.size.height)
        }
    }

}

extension BNPageView: UIScrollViewDelegate {
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        
        let ratio = scrollView.contentOffset.x / scrollView.bounds.size.width
        if ratio > CGFloat(self.viewControllers.count - 1) || ratio < 0 {
            return
        }

        let baseIndex = floorf(Float(ratio))
        let remainderRatio = ratio - CGFloat(baseIndex)

        if remainderRatio > -CGFloat.leastNormalMagnitude, remainderRatio < CGFloat.leastNormalMagnitude {
            let targetIndex = Int(baseIndex)

            let targetController = self.viewControllers[targetIndex]
            if targetController.parent == nil {
                targetController.willMove(toParent: parentViewController)
                parentViewController?.addChild(targetController)
                targetController.didMove(toParent: parentViewController)
                
                targetController.view.frame = CGRect(x: CGFloat(targetIndex) * scrollView.bounds.size.width, y: 0, width: scrollView.bounds.size.width, height: scrollView.bounds.size.height)
                contentView.addSubview(targetController.view)
            } else {
                if self.targetIndex != targetIndex {
                    targetController.beginAppearanceTransition(true, animated: true)
                    targetController.endAppearanceTransition()
                }
            }

            if self.targetIndex != targetIndex {
                let oldTargetController = self.viewControllers[self.targetIndex]
                let cachePage = oldTargetController.view.snapshotView(afterScreenUpdates: false)
                cachePage?.frame = oldTargetController.view.frame

                oldTargetController.beginAppearanceTransition(false, animated: true)
                oldTargetController.view.removeFromSuperview()
                oldTargetController.removeFromParent()
                oldTargetController.endAppearanceTransition()

                self.targetIndex = targetIndex
                self.currentIndex = targetIndex
                
            } else if self.currentIndex != targetIndex {
                let currentController = self.viewControllers[currentIndex]
                let cachePage = currentController.view.snapshotView(afterScreenUpdates: false)
                cachePage?.frame = currentController.view.frame
                
                currentController.view.removeFromSuperview()
                currentController.removeFromParent()
                currentController.endAppearanceTransition()
                
                self.currentIndex = targetIndex
            }

        } else {
            if abs(ratio - CGFloat(currentIndex)) > CGFloat.leastNormalMagnitude {
                var targetIndex = Int(baseIndex)
                if ratio > CGFloat(currentIndex) {
                    targetIndex = Int(baseIndex + 1)
                }

                if self.targetIndex != targetIndex {
                    let targetController = self.viewControllers[targetIndex]

                    if targetController.parent == nil {
                        targetController.willMove(toParent: parentViewController)
                        parentViewController?.addChild(targetController)

                        targetController.didMove(toParent: parentViewController)
                        targetController.view.frame = CGRect(x: CGFloat(targetIndex) * scrollView.bounds.size.width, y: 0, width: scrollView.bounds.size.width, height: scrollView.bounds.size.height)
                        contentView.addSubview(targetController.view)
                    } else {
                        targetController.beginAppearanceTransition(true, animated: true)
                        targetController.endAppearanceTransition()
                    }
                    self.targetIndex = targetIndex
                }
            }
        }
    }
}
